package com.sprouting.personal.controller;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sprouting.personal.annotations.ApiIdempotent;
import com.sprouting.personal.interfaces.common.Constant;
import com.sprouting.personal.model.common.ResultJson;
import com.sprouting.personal.model.common.jsr.AddGroup;
import com.sprouting.personal.model.common.jsr.UpdGroup;
import com.sprouting.personal.model.entity.PsPlan;
import com.sprouting.personal.model.entity.PsPlanHistory;
import com.sprouting.personal.model.vo.PlanSpan;
import com.sprouting.personal.model.vo.PlanStatistical;
import com.sprouting.personal.model.vo.PsPlanVo;
import com.sprouting.personal.model.vo.TaskOfDay;
import com.sprouting.personal.service.PsPlanHistoryService;
import com.sprouting.personal.service.PsPlanService;
import com.sprouting.personal.utils.CheckTool;
import com.sprouting.personal.utils.EntityTool;
import com.sprouting.personal.utils.IdTool;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 计划
 * 2023年7月1日21:18:26  如果在写这个模块的话，还是使用定时任务框架，cron表达式比较好
 *
 * @author ：LX（长沙麓谷）
 * @date 创建时间： 2023/1/10 17:46
 */
@RequestMapping("/plan")
@RestController
@Slf4j
public class PlanController {

    @Autowired
    private PsPlanService psPlanService;
    @Autowired
    private PsPlanHistoryService psPlanHistoryService;

    /*
        一些容易一起歧义的api说明示例

        偏移天数
            DateUtil.offsetDay(new Date(), 2) 假设今天是2023-9-22日，偏移2天后是9月24日
            DateUtil.offsetDay(new Date(), -2) 设置为负数则今天是 9月22，偏移后变成了 9月20日

        比较时间大小
            DateUtil.parseDateTime("2023-09-21 11:11:11").getTime() < DateUtil.parseDateTime("2023-09-22 11:11:11").getTime()
            上面的代码结果为 2023-09-21 11:11:11 小于 22号，时间越往后，毫秒数越大

        是否重置
            相差0天 DateUtil.betweenDay(DateUtil.parseDateTime("2023-09-21 23:23:23"), DateUtil.parseDateTime("2023-09-22 01:01:01"), false)
            相差1天 DateUtil.betweenDay(DateUtil.parseDateTime("2023-09-21 23:23:23"), DateUtil.parseDateTime("2023-09-22 01:01:01"), true)
            两者的区别，如果为true，则只要过了24点就算新的一天，为false则要满24小时才算一天

        取余
            long remainder = 相差的总年数 % 几年执行一次; 3年一周期的情况下，第一年，余数1，2年余数2，三年余数0，四年余数1，5年余数2,6年余数0
            0 % 3 = 0
            1 % 3 = 1
            2 % 3 = 2
            2 % 3 = 2
            3 % 3 = 0
            4 % 3 = 1
            5 % 3 = 2
            5 % 3 = 2
            6 % 3 = 0
            7 % 3 = 1

     */



    /**
     * 统计任务数据
     * @return
     */
    @PostMapping("/getPlanStatisticalQuantity")
    public ResultJson getPlanStatisticalQuantity(){
        List<Integer> taskFinishList = new ArrayList<>();
        taskFinishList.add(Constant.TaskFinish.UNACCOMPLISHED);
        taskFinishList.add(Constant.TaskFinish.OVERDUE);
        taskFinishList.add(Constant.TaskFinish.FINISH);
        taskFinishList.add(Constant.TaskFinish.OVERDUE_FINISH);
        taskFinishList.add(Constant.TaskFinish.STOP);
        List<PsPlan> planList = psPlanService.getPlanByUserId(StpUtil.getLoginIdAsString(), taskFinishList);

        PlanStatistical planStatistical = new PlanStatistical();
        if (CollUtil.isEmpty(planList)){
            return ResultJson.success(planStatistical);
        }

        Map<Integer, List<PsPlan>> collect = planList.stream().collect(Collectors.groupingBy(PsPlan::getFinish));

        // 今日任务数
        TaskOfDay taskOfDay = todayTaskHandle();
        planStatistical.setToday(taskOfDay.getPlanList().size());
        // 已逾期任务数
        if (CollUtil.isNotEmpty(collect.get(Constant.TaskFinish.OVERDUE))){
            planStatistical.setOverdue(collect.get(Constant.TaskFinish.OVERDUE).size());
        }
        // 任务总数
        if (CollUtil.isNotEmpty(planList)){
            planStatistical.setTool(planList.size());
        }
        // 已完成数
        if (CollUtil.isNotEmpty(collect.get(Constant.TaskFinish.FINISH))){
            planStatistical.setCompleted(collect.get(Constant.TaskFinish.FINISH).size());
        }
        // 已停止数量
        if (CollUtil.isNotEmpty(collect.get(Constant.TaskFinish.STOP))){
            planStatistical.setStop(collect.get(Constant.TaskFinish.STOP).size());
        }
        return ResultJson.success(planStatistical);
    }

    /**
     * 获取小时级别的任务-固定任务到秒级别的
     * 每3分钟前端调用一次,现在的这种方式并不是最优解，无法达到到了时间在提醒，但对于我们来说，够用了
     * @return
     */
    @GetMapping("/getHourTask")
    @ApiIdempotent
    public ResultJson getHourTask(){
        /*
            如果不加websocket，无法解决推送小时级别的计划,因为前端不可能正好在小时级别上，导致?但似乎也有一个问题，如何正好到时间了进行任务推送?
            因为没使用任务调度框架，现在干脆这么定，设定为3分钟执行一次。前端来发起请求，后端会将未来3分钟的小时级别循环的任务调度一遍，查询是否未来
            三分钟有任务会执行，有的话则会前端进行提示通知。

            其他非小时级别的任务，在进入到首页后调度一次。
         */

        TaskOfDay taskOfDay = todayTaskHandle();
        List<PsPlanVo> planList = taskOfDay.getPlanList();
        if (CollUtil.isEmpty(planList)){
            return ResultJson.success();
        }
        // 找到小时级别和定时的任务
         List<PsPlanVo> list = planList.stream().filter(obj -> Constant.TaskPlanType.LOOP_HOUR == obj.getType() || Constant.TaskPlanType.FIXED_YYYYMMDDHHMMSS == obj.getType()).collect(Collectors.toList());
        if (CollUtil.isEmpty(list)){
            return ResultJson.success();
        }
        // 定时的任务还要进行一次处理，检查是否在三分钟内
        List<PsPlanVo> loopHourList = planList.stream().filter(obj -> Constant.TaskPlanType.LOOP_HOUR == obj.getType()).collect(Collectors.toList());
        List<PsPlanVo> fixedList = planList.stream().filter(obj -> Constant.TaskPlanType.FIXED_YYYYMMDDHHMMSS == obj.getType()).collect(Collectors.toList());
        Date now = new Date();
        List<PsPlanVo> executeList = new ArrayList<>();
        if (CollUtil.isNotEmpty(fixedList)){
            // 检查开始时间是否在3分钟内，否则不予放到小时任务中
            for (PsPlanVo plan : fixedList) {
                DateTime dateTime = DateUtil.offsetMinute(plan.getStartTime(), -3);
                if (DateUtil.isIn(now, dateTime, plan.getStartTime())){
                    // 在三分钟内，要进行执行
                    executeList.add(plan);
                } else {
                    // 虽然今天执行，但还没预计的时间
                }
            }
        }

        if (CollUtil.isNotEmpty(loopHourList)){
            executeList.addAll(loopHourList);
        }

        return ResultJson.success(executeList);
    }

    /**
     * 终止计划
     * @param id 计划ID
     * @return
     */
    @PostMapping("/stopPlan")
    @ApiIdempotent
    public ResultJson stopPlan(String id){
        if (CheckTool.checkNull(id)){
            return ResultJson.errorIllegal();
        }
        PsPlan plan = psPlanService.getPlanById(id);
        if (plan == null){
            return ResultJson.errorIllegal();
        }
        if (Constant.TaskFinish.STOP.equals(plan.getFinish())
                || Constant.TaskFinish.OVERDUE_FINISH.equals(plan.getFinish())
                || Constant.TaskFinish.FINISH.equals(plan.getFinish())){
            return ResultJson.success();
        }
        plan.setFinish(Constant.TaskFinish.STOP);
        EntityTool.buildUpdBase(plan, StpUtil.getLoginIdAsString());
        psPlanService.updateById(plan);
        return ResultJson.success();
    }

    /**
     * 完成一次计划
     * @param id 计划ID
     * @return
     */
    @PostMapping("/planComplete")
    @ApiIdempotent
    public ResultJson planComplete(String id){
        if (CheckTool.checkNull(id)){
            return ResultJson.errorIllegal();
        }
        PsPlan plan = psPlanService.getPlanById(id);
        if (plan == null){
            return ResultJson.errorIllegal();
        }
        if (Constant.TaskFinish.STOP.equals(plan.getFinish())
                || Constant.TaskFinish.FINISH.equals(plan.getFinish())
                || Constant.TaskFinish.OVERDUE_FINISH.equals(plan.getFinish())){
            return ResultJson.success("任务已完成");
        }
        // 需要判断是否任务时间中
        TaskOfDay taskOfDay = todayTaskHandle();
        List<PsPlanVo> planList = taskOfDay.getPlanList();
        Map<String, PsPlanVo> map = planList.stream().collect(Collectors.toMap(PsPlanVo::getId, Function.identity()));
        if (map.get(id) != null){
            // 属于今天的任务
            PsPlanVo psPlanVo = map.get(id);
            // 填录计算出来的执行时间
            psPlanVo.setExecuteLastTime(psPlanVo.getExecTime());
            EntityTool.buildUpdBase(psPlanVo, StpUtil.getLoginIdAsString());
            PsPlanHistory psPlanHistory = buildPlanHistory(psPlanVo);
            psPlanHistory.setFinishTime(psPlanVo.getExecuteLastTime());
            psPlanService.updatePlanAndHistory(psPlanVo, psPlanHistory);

        } else {
            // 再一次获取，防止上面进行了刷新没更新.一般进入到这里的是逾期的
            plan = psPlanService.getPlanById(id);
            if (Constant.TaskFinish.OVERDUE.equals(plan.getFinish())){
                Map<String, PsPlanVo> allMap = taskOfDay.getAllPlanList().stream().collect(Collectors.toMap(PsPlanVo::getId, Function.identity()));
                if (allMap.get(id) != null){
                    PsPlanVo psPlanVo = allMap.get(id);
                    // 填录计算出来的执行时间
                    psPlanVo.setExecuteLastTime(psPlanVo.getExecTime());
                    EntityTool.buildUpdBase(psPlanVo, StpUtil.getLoginIdAsString());
                    PsPlanHistory psPlanHistory = buildPlanHistory(psPlanVo);
                    psPlanHistory.setFinishTime(psPlanVo.getExecuteLastTime());
                    psPlanService.updatePlanAndHistory(psPlanVo, psPlanHistory);

                } else {
                    return ResultJson.error("任务不需要执行");
                }

            } else {
                return ResultJson.error("任务无需执行");
            }
        }

        // 更新状态
        todayTaskHandle();
        return ResultJson.success();
    }

    /**
     * 全部任务查询分页
     * @return
     */
    @PostMapping("/getTaskAll")
    public ResultJson getTaskAll(Page page, PsPlan plan){
        return ResultJson.success(psPlanService.getTaskAll(page, plan, StpUtil.getLoginIdAsString()));
    }

    /**
     * 分页查询任务执行记录
     * @param page 分页参数
     * @param planId 计划ID
     * @return
     */
    @PostMapping("/getPlanHistory")
    public ResultJson getPlanHistory(Page<PsPlanHistory> page, String planId){
        if (CheckTool.checkNull(planId)){
            return ResultJson.errorIllegal();
        }

        return ResultJson.success(psPlanHistoryService.getTaskHistory(page, planId));
    }

    /**
     * 找到已完成和终止的任务
     * @return
     */
    @GetMapping("/getCompleteTask")
    public ResultJson getCompleteTask(){
        List<Integer> taskFinishList = new ArrayList<>();
        taskFinishList.add(Constant.TaskFinish.FINISH);
        taskFinishList.add(Constant.TaskFinish.STOP);
        taskFinishList.add(Constant.TaskFinish.OVERDUE_FINISH);
        List<PsPlan> planList = psPlanService.getPlanByUserId(StpUtil.getLoginIdAsString(), taskFinishList);
        return ResultJson.success(planList);
    }

    /**
     * 找到逾期未完成的任务（不含逾期已完成）
     * @return
     */
    @GetMapping("/getOverdueTask")
    public ResultJson getOverdueTask(){
        List<Integer> taskFinishList = new ArrayList<>();
        taskFinishList.add(Constant.TaskFinish.OVERDUE);
        List<PsPlan> planList = psPlanService.getPlanByUserId(StpUtil.getLoginIdAsString(), taskFinishList);
        return ResultJson.success(planList);
    }

    /**
     * 获取今天的待办任务，会考虑提前提醒的情况
     * 这里的数据最终颗粒度还是在天级别，循环任务中的小时级别的是否到点了以及签收任务需要其他的判断
     * @return
     */
    @GetMapping("/getTodayTask")
    public ResultJson getTodayTask(){
        // 刷新任务
        // refreshPlan();
        return ResultJson.success(todayTaskHandle());
    }

    /**
     * 获取最近N天要办的任务-不考虑提前提醒的情况
     * @param recentTime 天数，默认为30天
     * @return
     */
    @PostMapping("/getCalendarTask")
    public ResultJson getCalendarTask(Integer recentTime){
        if (recentTime == null){
            recentTime = 30;
        }
        if (recentTime < 1){
            recentTime = 1;
        }
        String userId = StpUtil.getLoginIdAsString();
        // 查询出需要完成的任务
        List<Integer> taskFinishList = new ArrayList<>();
        taskFinishList.add(Constant.TaskFinish.UNACCOMPLISHED);
        taskFinishList.add(Constant.TaskFinish.OVERDUE);
        taskFinishList.add(Constant.TaskFinish.RUN);
        List<PsPlan> planList = psPlanService.getPlanByUserId(userId, taskFinishList);

        // 过滤已完成的
        // planList = removeCompletedTask(planList);
        List<PsPlanVo> psPlanVoList = BeanUtil.copyToList(planList, PsPlanVo.class);


        Date now = new Date();
        PlanSpan planSpan = new PlanSpan();
        planSpan.setStartTime(DateUtil.formatDate(DateUtil.beginOfWeek(now)));
        // 这里要有周为周期，因为日期表那里不支持非周的周期
        planSpan.setEndTime(DateUtil.formatDate(DateUtil.endOfWeek(DateUtil.offsetDay(now, recentTime))));


        List<TaskOfDay> taskOfDayList = new ArrayList<>();
        // 时间范围(执行结果都是一样的，都是对日期进行操作，如果只是使用.add()方法执行，结果都一样，因为都是将日期+1；但是DAY_OF_*的实际用处并不在修改日期，而是在获取具体的天数。)
        List<DateTime> dateRangeList = DateUtil.rangeToList(now, DateUtil.offsetDay(now, recentTime), DateField.DAY_OF_MONTH);
        for (DateTime dateTime : dateRangeList){
            TaskOfDay taskOfDay = new TaskOfDay();
            taskOfDay.setTimeStr(DateUtil.formatDate(dateTime));
            taskOfDay.setTime(dateTime);
            for (PsPlanVo plan : psPlanVoList){
                // 不需要考虑提前时间
                Integer type = plan.getType();
                switch (type){
                    case Constant.TaskPlanType.FIXED_YYMMDD:
                    case Constant.TaskPlanType.FIXED_YYYYMMDDHHMMSS:
                        // 固定时间和固定时间到秒的同一个逻辑
                        if (DateUtil.formatDate(plan.getStartTime()).equals(taskOfDay.getTimeStr())){
                            taskOfDay.getPlanList().add(plan);
                        }
                        break;

                    case Constant.TaskPlanType.LOOP_YEAR:
                        String startTimeFormat = DateUtil.format(plan.getStartTime(), "MM-dd");
                        // 先判断是否在今天这个日期
                        if (!startTimeFormat.equals(DateUtil.format(dateTime, "MM-dd"))){
                            // 说明这个任务不在今天，不进行继续判断
                            // 注意，这里会有一个隐藏的bug，比如四年一闰的情况下，如果配置在了闰年的日期上，可能会导致某个时间只有4年才执行一次的，但如果又配置了执行周期，那什么时候执行就不好说了
                            continue;
                        }
                        // 检查是否已经开始任务，如果开始未开始不进行判断
                        if (plan.getStartTime().getTime() > dateTime.getTime()){
                            // 任务还未开始
                            continue;
                        }
                        if (plan.getEndTime() != null){
                            if (plan.getEndTime().getTime() < dateTime.getTime()){
                                // 任务已经执行完成
                                continue;
                            }
                        }
                        // 判断周期是正好匹配
                        // 最后一个参数的含义，当为true的时候，比如现在是2022年1月10日，目标时间是2025年1月11日，那么算3年，即时是2025-01-09，也是三年。但如果是false的时候,2025-01-09则只有2年，2025年1月10日后才是3年
                        // 而我们在理解这里的时候，每3年执行一次，是必须满了3年，也就是不重置的形式
                        if (plan.getCycle() < 2){
                            // 这里小于2，其实就是周期为1，周期为1的话，上面的条件都满足，那这一年就是要处于要执行的年份，上面的equals已经对应上了，这里使用2，是防止非法数据，比如负数和0
                            // 防止为0等情况
                            taskOfDay.getPlanList().add(plan);
                        } else {
                            // 不会重置年，6月份开始到了6月份才是一年
                            long cycle = DateUtil.betweenYear(dateTime, plan.getStartTime(), false);
                            // 取余运算
                            // 3年一周期的情况下，第一年，余数1，2年余数2，三年余数0，四年余数1，5年余数2,6年余数0
                            long remainder = cycle % plan.getCycle();
                            if (remainder == 0){
                                taskOfDay.getPlanList().add(plan);
                            }
                        }

                        break;

                    case Constant.TaskPlanType.LOOP_MONTH:
                        String startTimeDayFormat = DateUtil.format(plan.getStartTime(), "dd");
                        if (!startTimeDayFormat.equals(DateUtil.format(dateTime, "dd"))){
                            // 就不在同一天，没必须继续判断了，假设每个月进行一次，那也应该是在同一天
                            // 注意：这里同样会有一个隐藏问题，假设设定的是30号，但2月份没有30号，故而会导致2月份的30号任务丢失
                            continue;
                        }
                        // 检查是否已经开始任务，如果开始未开始不进行判断
                        if (plan.getStartTime().getTime() > dateTime.getTime()){
                            // 任务还未开始
                            continue;
                        }
                        if (plan.getEndTime() != null){
                            if (plan.getEndTime().getTime() < dateTime.getTime()){
                                // 任务已经执行完成，已逾期或者
                                continue;
                            }
                        }
                        if (plan.getCycle() < 2){
                            // 防止为0等情况。其实就是周期为1，周期为1的话，上面的条件都满足，那这一年就是要处于要执行的年份，上面的equals已经对应上了，这里使用2，是防止非法数据，比如负数和0
                            taskOfDay.getPlanList().add(plan);
                        } else {
                            long cycle = DateUtil.betweenMonth(dateTime, plan.getStartTime(), false);
                            // 取余运算
                            // // 3年一周期的情况下，第一年，余数1，2年余数2，三年余数0，四年余数1，5年余数2,6年余数0
                            long remainder = cycle % plan.getCycle();
                            if (remainder == 0){
                                taskOfDay.getPlanList().add(plan);
                            }
                        }

                        break;

                    case Constant.TaskPlanType.LOOP_DAY:
                        /// 不需要检查是否到了指定天，这里只要检测是否在周期中
                        // 检查是否已经开始任务，如果开始未开始不进行判断
                        if (plan.getStartTime().getTime() > dateTime.getTime()){
                            // 任务还未开始
                            continue;
                        }
                        if (plan.getEndTime() != null){
                            if (plan.getEndTime().getTime() < dateTime.getTime()){
                                // 任务已经执行完成，已逾期或者
                                continue;
                            }
                        }
                        // 判断当天是否已经执行了
                        PsPlanHistory latestHistory = psPlanHistoryService.getLatestRecord(plan.getId());
                        if (latestHistory != null){
                            if (DateUtil.isSameDay(latestHistory.getFinishTime(), taskOfDay.getTime())){
                                // 同一天则执行过了
                                continue;
                            }
                        }
                        if (plan.getCycle() < 2){
                            // 防止为0等情况。这里就等于一天一次
                            taskOfDay.getPlanList().add(plan);
                        } else {
                            long cycle = DateUtil.betweenDay(dateTime, plan.getStartTime(), false);
                            // 取余运算
                            long remainder = cycle % plan.getCycle();
                            if (remainder == 0){
                                taskOfDay.getPlanList().add(plan);
                            }
                        }
                        break;

                    case Constant.TaskPlanType.LOOP_HOUR:
                        if (plan.getStartTime() != null){
                            if (plan.getStartTime().getTime() > dateTime.getTime()){
                                // 任务还未开始
                                continue;
                            }
                        }
                        if (plan.getEndTime() != null){
                            if (plan.getEndTime().getTime() < dateTime.getTime()){
                                // 任务已经执行完成，已逾期或者
                                continue;
                            }
                        }
                        // 不需要计算周期，因为这里是按照天来的，直接这一天肯定是有任务
                        taskOfDay.getPlanList().add(plan);
                        break;
                    default:
                        break;
                }
            }
            taskOfDayList.add(taskOfDay);
        }

        planSpan.setTaskOfDayList(taskOfDayList);
        return ResultJson.success(planSpan);
    }

    /**
     * 根据计划ID删除计划以及执行历史记录
     * @param id 计划ID
     * @return
     */
    @PostMapping("/delPlanById")
    @ApiIdempotent
    public ResultJson delPlanById(String id){
        if (CheckTool.checkNull(id)){
            return ResultJson.errorIllegal();
        }
        psPlanService.delPlan(id);
        return ResultJson.success();
    }

    /**
     * 保存任务计划
     * @param plan 任务计划
     * @return
     */
    @PostMapping("/savePlan")
    @ApiIdempotent
    public ResultJson savePlan(@Validated(AddGroup.class) PsPlan plan){
        String msg = checkPlan(plan);
        if (msg != null){
            return ResultJson.error(msg);
        }

        plan.setId(IdTool.getId());
        plan.setUserId(StpUtil.getLoginIdAsString());
        plan.setFinish(Constant.TaskFinish.UNACCOMPLISHED);
        EntityTool.buildSaveBase(plan, StpUtil.getLoginIdAsString());
        psPlanService.save(plan);
        return ResultJson.success();
    }

    /**
     * 更新任务计划
     * @param plan 任务计划
     * @return
     */
    @PostMapping("/updPlan")
    @ApiIdempotent
    public ResultJson updPlan(@Validated(UpdGroup.class) PsPlan plan){
        String msg = checkPlan(plan);
        if (msg != null){
            return ResultJson.error(msg);
        }

        EntityTool.buildUpdBase(plan, StpUtil.getLoginIdAsString());
        psPlanService.updateById(plan);
        return ResultJson.success();
    }

    /**
     * 校验任务计划并进行数据补充
     * @param plan 任务计划信息
     * @return 非null则是有错误数据
     */
    private String checkPlan(PsPlan plan){
        if (plan.getImportance() == null){
            plan.setImportance(Constant.ImportanceLevel.NO_IMPORTANT_NO_EMERGENCY);
        }
        if (plan.getClassify() == null){
            plan.setClassify(Constant.TaskClassify.PERSONAL);
        }
        if (plan.getAdvance() == null){
            plan.setAdvance(0);
        }
        if (plan.getCycle() == null){
            plan.setCycle(0);
        }
        long now = System.currentTimeMillis();
        if (now > plan.getStartTime().getTime()){
            if (DateUtil.isSameDay(new Date(), plan.getStartTime())){

            } else {
                return "任务开始时间不能小于当前时间";
            }
        }
        if (plan.getEndTime() != null){
            if (plan.getStartTime().getTime() > plan.getEndTime().getTime()){
                return "任务开始时间不能小于任务结束时间";
            }
        }

        switch (plan.getType()){
            case Constant.TaskPlanType.FIXED_YYMMDD:
            case Constant.TaskPlanType.FIXED_YYYYMMDDHHMMSS:
                plan.setCycle(0);
                plan.setEndTime(plan.getStartTime());
                break;
            case Constant.TaskPlanType.LOOP_YEAR:
                if (plan.getAdvance() > 365){
                    return "提前提醒不能超过365天";
                }
                break;
            case Constant.TaskPlanType.LOOP_MONTH:
                if (plan.getAdvance() > 28){
                    return "提前提醒不能超过28天";
                }
                break;
            case Constant.TaskPlanType.LOOP_DAY:
            case Constant.TaskPlanType.LOOP_HOUR:
                // 到了天级别就不允许提前提醒了
                plan.setAdvance(0);
                break;
            default:
                return "非法的任务类型";
        }

        return null;
    }

    /**
     * 生成历史记录
     * @param plan 计划
     * @return
     */
    private PsPlanHistory buildPlanHistory(PsPlan plan){
        PsPlanHistory psPlanHistory = new PsPlanHistory();
        psPlanHistory.setId(IdTool.getId());
        psPlanHistory.setUserId(StpUtil.getLoginIdAsString());
        psPlanHistory.setPlanId(plan.getId());
        // 这个时间到外面在添加，这里还是希望保证和计划的一样
        psPlanHistory.setFinishTime(plan.getExecuteLastTime());
        EntityTool.buildSaveBase(psPlanHistory, StpUtil.getLoginIdAsString());
        return psPlanHistory;
    }

    /**
     * 获取今天的待办任务（包含提前的）逾期的不会查询出来
     * 本方法的目标
     * 1、对数据状态进行更新管理
     * 2、获取今天，包括提前的任务。加标记
     * 3、获取到本次执行的时间
     * 更新进度，将未完成的逾期的进行处理，要废弃掉 refreshPlan 方法
     * @return
     */
    private TaskOfDay todayTaskHandle(){
        String userId = StpUtil.getLoginIdAsString();
        // 查询出需要完成的任务
        List<Integer> taskFinishList = new ArrayList<>();
        taskFinishList.add(Constant.TaskFinish.UNACCOMPLISHED);
        taskFinishList.add(Constant.TaskFinish.OVERDUE);
        taskFinishList.add(Constant.TaskFinish.RUN);
        List<PsPlan> planList = psPlanService.getPlanByUserId(userId, taskFinishList);

        // 过滤已完成的
        // planList = removeCompletedTask(planList);

        Date now = new Date();
        TaskOfDay taskOfDay = new TaskOfDay();
        taskOfDay.setTimeStr(DateUtil.formatDate(now));

        if (CollUtil.isEmpty(planList)){
            return taskOfDay;
        }
        List<PsPlanVo> psPlanVoList = BeanUtil.copyToList(planList, PsPlanVo.class);

        for (PsPlanVo plan : psPlanVoList){
            Integer type = plan.getType();
            taskOfDay.getAllPlanList().add(plan);
            switch (type){
                case Constant.TaskPlanType.FIXED_YYMMDD:
                    plan.setExecTime(plan.getStartTime());
                    if (plan.getExecuteLastTime() != null){
                        // 说明已经执行过了，直接修改为结束
                        plan.setFinish(Constant.TaskFinish.FINISH);
                        EntityTool.buildUpdBase(plan, userId);
                        psPlanService.updateById(plan);
                        continue;
                    }
                    // 如果是当天，则为运行中，如果是提前的，则显示未运行中
                    if (DateUtil.isSameDay(plan.getStartTime(), now)){
                        plan.setFinish(Constant.TaskFinish.RUN);
                        EntityTool.buildUpdBase(plan, userId);
                        psPlanService.updateById(plan);
                        taskOfDay.getPlanList().add(plan);
                        continue;
                    }
                    if (DateUtil.endOfDay(plan.getStartTime()).getTime() < now.getTime()){
                        // 说明逾期
                        plan.setFinish(Constant.TaskFinish.OVERDUE);
                        EntityTool.buildUpdBase(plan, userId);
                        psPlanService.updateById(plan);
                        continue;
                    }

                    // 判断是否在提前范围中
                    DateTime advanceTime = DateUtil.offsetDay(plan.getStartTime(), -plan.getAdvance());
                    if (DateUtil.isIn(now, advanceTime, plan.getStartTime())){
                        // 在范围内进行提示
                        taskOfDay.getPlanList().add(plan);

                    } else {
                        // 还没到该执行的时机
                    }
                    break;

                case Constant.TaskPlanType.FIXED_YYYYMMDDHHMMSS:
                    plan.setExecTime(plan.getStartTime());
                    if (plan.getExecuteLastTime() != null){
                        // 说明已经执行过了，直接修改为结束
                        plan.setFinish(Constant.TaskFinish.FINISH);
                        EntityTool.buildUpdBase(plan, userId);
                        psPlanService.updateById(plan);
                        continue;
                    }
                    // 如果是当天，在当天并且没超过最后时间，则是运行中
                    if (DateUtil.isIn(now, DateUtil.beginOfDay(plan.getStartTime()), plan.getStartTime())){
                        plan.setFinish(Constant.TaskFinish.RUN);
                        EntityTool.buildUpdBase(plan, userId);
                        psPlanService.updateById(plan);
                        taskOfDay.getPlanList().add(plan);
                        continue;
                    }
                    if (plan.getStartTime().getTime() < now.getTime()){
                        // 说明逾期
                        plan.setFinish(Constant.TaskFinish.OVERDUE);
                        EntityTool.buildUpdBase(plan, userId);
                        psPlanService.updateById(plan);
                        continue;
                    }

                    // 判断是否在提前范围中
                    DateTime advanceTimeAccurate = DateUtil.offsetDay(plan.getStartTime(), -plan.getAdvance());
                    if (DateUtil.isIn(now, advanceTimeAccurate, plan.getStartTime())){
                        // 在范围内进行提示
                        taskOfDay.getPlanList().add(plan);

                    } else {
                        // 还没到该执行的时机
                    }

                    break;

                case Constant.TaskPlanType.LOOP_YEAR:
                    // 获取下次执行的时间段
                    // 下次开始
                    Date nextStart;
                    // 下次结束
                    Date nextEnd;
                    if (plan.getExecuteLastTime() == null){
                        // 从未执行过，第一次就是开始时间
                        nextStart = DateUtil.offsetDay(plan.getStartTime(), -plan.getAdvance());
                        nextEnd = plan.getStartTime();
                        plan.setExecTime(plan.getStartTime());

                    } else {
                        // 已经执行过
                        // 获取下次应该执行的时间
                        DateTime offsetTime = DateUtil.offset(plan.getExecuteLastTime(), DateField.YEAR, plan.getCycle());
                        nextStart = DateUtil.offsetDay(offsetTime, -plan.getAdvance());
                        nextEnd = offsetTime;
                        plan.setExecTime(offsetTime);
                    }

                    if (DateUtil.isIn(now, nextStart, nextEnd)){
                        // 正好在执行周期中
                        plan.setFinish(Constant.TaskFinish.RUN);
                        EntityTool.buildUpdBase(plan, userId);
                        psPlanService.updateById(plan);
                        taskOfDay.getPlanList().add(plan);
                        continue;
                    }
                    if (now.getTime() < nextStart.getTime()){
                        // 还没到执行时间
                        plan.setFinish(Constant.TaskFinish.UNACCOMPLISHED);
                        EntityTool.buildUpdBase(plan, userId);
                        psPlanService.updateById(plan);
                        continue;
                    }
                    if (nextEnd.getTime() < now.getTime()){
                        if (plan.getEndTime() != null){
                            if (plan.getEndTime().getTime() < nextEnd.getTime()){
                                // 最后一次已经超过最大执行时间，为已完成的
                                plan.setFinish(Constant.TaskFinish.FINISH);
                                EntityTool.buildUpdBase(plan, userId);
                                psPlanService.updateById(plan);
                                continue;
                            }
                        }
                        // 逾期了
                        plan.setFinish(Constant.TaskFinish.OVERDUE);
                        EntityTool.buildUpdBase(plan, userId);
                        psPlanService.updateById(plan);
                        continue;
                    }

                    break;

                case Constant.TaskPlanType.LOOP_MONTH:
                    // 下次开始
                    Date nextStartMonth;
                    // 下次结束
                    Date nextEndMonth;
                    if (plan.getExecuteLastTime() == null){
                        // 从未执行过，第一次就是开始时间
                        nextStartMonth = DateUtil.offsetDay(plan.getStartTime(), -plan.getAdvance());
                        nextEndMonth = plan.getStartTime();
                        plan.setExecTime(plan.getStartTime());

                    } else {
                        // 已经执行过
                        // 获取下次应该执行的时间
                        DateTime offsetTime = DateUtil.offset(plan.getExecuteLastTime(), DateField.MONTH, plan.getCycle());
                        nextStartMonth = DateUtil.offsetDay(offsetTime, -plan.getAdvance());
                        nextEndMonth = offsetTime;
                        plan.setExecTime(offsetTime);
                    }

                    if (DateUtil.isIn(now, nextStartMonth, nextEndMonth)){
                        // 正好在执行周期中
                        plan.setFinish(Constant.TaskFinish.RUN);
                        EntityTool.buildUpdBase(plan, userId);
                        psPlanService.updateById(plan);
                        taskOfDay.getPlanList().add(plan);
                        continue;
                    }
                    if (now.getTime() < nextStartMonth.getTime()){
                        // 还没到执行时间
                        plan.setFinish(Constant.TaskFinish.UNACCOMPLISHED);
                        EntityTool.buildUpdBase(plan, userId);
                        psPlanService.updateById(plan);
                        continue;
                    }
                    if (nextEndMonth.getTime() < now.getTime()){
                        if (plan.getEndTime() != null){
                            if (plan.getEndTime().getTime() < nextEndMonth.getTime()){
                                // 最后一次已经超过最大执行时间，为已完成的
                                plan.setFinish(Constant.TaskFinish.FINISH);
                                EntityTool.buildUpdBase(plan, userId);
                                psPlanService.updateById(plan);
                                continue;
                            }
                        }
                        // 逾期了
                        plan.setFinish(Constant.TaskFinish.OVERDUE);
                        EntityTool.buildUpdBase(plan, userId);
                        psPlanService.updateById(plan);
                        continue;
                    }

                    break;

                case Constant.TaskPlanType.LOOP_DAY:
                    // 下次开始
                    Date nextStartDay;
                    // 下次结束
                    Date nextEndDay;
                    if (plan.getExecuteLastTime() == null){
                        // 从未执行过，第一次就是开始时间
                        // nextStartDay = DateUtil.offsetDay(plan.getStartTime(), -plan.getAdvance());
                        // nextEndDay = plan.getStartTime();
                        // 无提醒时间了，开始时间就是当天的开始
                        nextStartDay = DateUtil.beginOfDay(plan.getStartTime());
                        nextEndDay = DateUtil.endOfDay(plan.getStartTime());
                        plan.setExecTime(plan.getStartTime());

                    } else {
                        // 已经执行过
                        // 获取下次应该执行的时间
                        DateTime offsetTime = DateUtil.offset(plan.getExecuteLastTime(), DateField.DAY_OF_YEAR, plan.getCycle());
                        nextStartDay = DateUtil.beginOfDay(offsetTime);
                        nextEndDay = DateUtil.endOfDay(offsetTime);
                        plan.setExecTime(offsetTime);
                    }

                    if (DateUtil.isIn(now, nextStartDay, nextEndDay)){
                        // 正好在执行周期中
                        plan.setFinish(Constant.TaskFinish.RUN);
                        EntityTool.buildUpdBase(plan, userId);
                        psPlanService.updateById(plan);
                        taskOfDay.getPlanList().add(plan);
                        continue;
                    }
                    if (now.getTime() < nextStartDay.getTime()){
                        // 还没到执行时间
                        plan.setFinish(Constant.TaskFinish.UNACCOMPLISHED);
                        EntityTool.buildUpdBase(plan, userId);
                        psPlanService.updateById(plan);
                        continue;
                    }
                    if (nextEndDay.getTime() < now.getTime()){
                        if (plan.getEndTime() != null){
                            if (plan.getEndTime().getTime() < nextEndDay.getTime()){
                                // 最后一次已经超过最大执行时间，为已完成的
                                plan.setFinish(Constant.TaskFinish.FINISH);
                                EntityTool.buildUpdBase(plan, userId);
                                psPlanService.updateById(plan);
                                continue;
                            }
                        }
                        // 逾期了
                        plan.setFinish(Constant.TaskFinish.OVERDUE);
                        EntityTool.buildUpdBase(plan, userId);
                        psPlanService.updateById(plan);
                        continue;
                    }

                    break;

                case Constant.TaskPlanType.LOOP_HOUR:
                    // 下次开始
                    Date nextStartHour;
                    // 下次结束,小时的如果都按照精确时间去算的话，就全部是逾期了，直接给提前3分钟，3分钟内的都算要做的，因为是小时级别的提醒，不用担心
                    Date nextEndHour;
                    if (plan.getExecuteLastTime() == null){
                        // 从未执行过，第一次就是开始时间
                        // 默认给提前3分钟，否则全是逾期
                        nextStartHour = DateUtil.offsetMinute(plan.getStartTime(), -3);
                        nextEndHour = plan.getStartTime();
                        plan.setExecTime(plan.getStartTime());

                    } else {
                        // 已经执行过
                        // 获取下次应该执行的时间
                        DateTime offsetTime = DateUtil.offset(plan.getExecuteLastTime(), DateField.HOUR, plan.getCycle());
                        // 默认给提前3分钟，否则全是逾期
                        nextStartHour = DateUtil.offsetMinute(offsetTime, -3);
                        nextEndHour = offsetTime;
                        plan.setExecTime(offsetTime);
                    }

                    if (DateUtil.isIn(now, nextStartHour, nextEndHour)){
                        // 正好在执行周期中
                        plan.setFinish(Constant.TaskFinish.RUN);
                        EntityTool.buildUpdBase(plan, userId);
                        psPlanService.updateById(plan);
                        taskOfDay.getPlanList().add(plan);
                        continue;
                    }
                    if (now.getTime() < nextStartHour.getTime()){
                        // 还没到执行时间
                        plan.setFinish(Constant.TaskFinish.UNACCOMPLISHED);
                        EntityTool.buildUpdBase(plan, userId);
                        psPlanService.updateById(plan);
                        continue;
                    }
                    if (nextEndHour.getTime() < now.getTime()){
                        if (plan.getEndTime() != null){
                            if (plan.getEndTime().getTime() < nextEndHour.getTime()){
                                // 最后一次已经超过最大执行时间，为已完成的
                                plan.setFinish(Constant.TaskFinish.FINISH);
                                EntityTool.buildUpdBase(plan, userId);
                                psPlanService.updateById(plan);
                                continue;
                            }
                        }
                        // 逾期了
                        plan.setFinish(Constant.TaskFinish.OVERDUE);
                        EntityTool.buildUpdBase(plan, userId);
                        psPlanService.updateById(plan);
                        continue;
                    }

                    break;

                default:
                    break;
            }
        }

        return taskOfDay;
    }

    /**
     * 切换任务状态
     * @param plan 计划
     */
    private void changeTaskFinish(PsPlan plan){
        if (Constant.TaskFinish.STOP.equals(plan.getFinish())
                || Constant.TaskFinish.FINISH.equals(plan.getFinish())
                || Constant.TaskFinish.OVERDUE_FINISH.equals(plan.getFinish())){
            return;
        }

        if (Constant.TaskPlanType.FIXED_YYMMDD == plan.getType() || Constant.TaskPlanType.FIXED_YYYYMMDDHHMMSS == plan.getType()){
            // 固定任务的情况
            if (Constant.TaskFinish.UNACCOMPLISHED.equals(plan.getFinish())){
                plan.setFinish(Constant.TaskFinish.FINISH);
            } else if (Constant.TaskFinish.OVERDUE.equals(plan.getFinish())){
                plan.setFinish(Constant.TaskFinish.OVERDUE_FINISH);
            } else {
                log.error("未知的异常情况，数据状态无法更改");
            }

        } else {
            // 循环任务
            if (Constant.TaskFinish.OVERDUE.equals(plan.getFinish())){
                // 循环任务就在已逾期和未完成之间切换
                plan.setFinish(Constant.TaskFinish.UNACCOMPLISHED);
            }
        }

    }

    /**
     * 过滤掉已经执行完成的任务，注意，只是简单过滤，增加了复杂调度周期的过滤效果并不好
     * @param planList 要过滤的计划
     * @return 没有完成的计划和任务
     */
    private List<PsPlan> removeCompletedTask(List<PsPlan> planList){
        List<PsPlan> filterList = new ArrayList<>();
        if (CollUtil.isEmpty(planList)){
            return filterList;
        }

        for (PsPlan plan : planList){
            Integer type = plan.getType();
            switch (type){
                case Constant.TaskPlanType.FIXED_YYMMDD:
                case Constant.TaskPlanType.FIXED_YYYYMMDDHHMMSS:
                    // 固定任务的一般不存在已经执行还在这里的情况
                    filterList.add(plan);
                    break;

                case Constant.TaskPlanType.LOOP_YEAR:
                    // 如何判断本次的任务已经执行完成了，年和月的循环还好判断，小时级别的循环有点麻烦，不，只做简单的判断也简单，只要判定是否超过一年或者一个小时即可
                    if (plan.getExecuteLastTime() != null){
                        long year = DateUtil.betweenYear(plan.getExecuteLastTime(), new Date(), false);
                        if (year < 1){
                            continue;
                        }
                    }
                    filterList.add(plan);
                    break;

                case Constant.TaskPlanType.LOOP_MONTH:
                    if (plan.getExecuteLastTime() != null){
                        long month = DateUtil.betweenMonth(plan.getExecuteLastTime(), new Date(), false);
                        if (month < 1){
                            continue;
                        }
                    }
                    filterList.add(plan);
                    break;

                case Constant.TaskPlanType.LOOP_DAY:
                    if (plan.getExecuteLastTime() != null){
                        long day = DateUtil.betweenDay(plan.getExecuteLastTime(), new Date(), false);
                        if (day < 1){
                            continue;
                        }
                    }
                    filterList.add(plan);
                    break;

                case Constant.TaskPlanType.LOOP_HOUR:
                    if (plan.getExecuteLastTime() != null){
                        long hour = DateUtil.between(plan.getExecuteLastTime(), new Date(), DateUnit.HOUR, false);
                        if (hour < 1){
                            continue;
                        }
                    }
                    filterList.add(plan);;
                    break;

                default:
                    break;
            }
        }

        return filterList;
    }

    /**
     * 刷新任务，将待完成却没有完成的任务完成
     * 这里不需要进行事务控制
     */
    @ApiIdempotent
    private void refreshPlan(){
        String userId = StpUtil.getLoginIdAsString();
        // 查询出需要完成的任务
        List<Integer> taskFinishList = new ArrayList<>();
        taskFinishList.add(Constant.TaskFinish.UNACCOMPLISHED);

        Date now = new Date();
        int nowDd = Integer.parseInt(DateUtil.format(now, "dd"));


        List<PsPlan> planList = psPlanService.getPlanByUserId(userId, taskFinishList);
        if (CollUtil.isEmpty(planList)){
            return;
        }
        for (PsPlan plan : planList){
            Integer type = plan.getType();
            plan.setFinish(Constant.TaskFinish.OVERDUE);
            EntityTool.buildUpdBase(plan, userId);

            switch (type){
                case Constant.TaskPlanType.FIXED_YYMMDD:
                case Constant.TaskPlanType.FIXED_YYYYMMDDHHMMSS:
                    if (plan.getStartTime().getTime() < System.currentTimeMillis()){
                        // 说明逾期
                        psPlanService.updateById(plan);
                    }
                    break;

                case Constant.TaskPlanType.LOOP_YEAR:
                    if (plan.getExecuteLastTime() != null){
                        // 执行过至少一次，检查2者的差值即可
                        long year = DateUtil.betweenYear(plan.getExecuteLastTime(), new Date(), false);
                        // 大于1则是要可能要执行的
                        if (year > 1){
                            // 大于周期，则有未执行的事情
                            if (year > plan.getCycle()){
                                psPlanService.updateById(plan);
                            }
                        }
                    } else {
                        // 从未执行过
                        long year = DateUtil.betweenYear(plan.getStartTime(), new Date(), false);
                        if (year > 1){
                            if (year > plan.getCycle()){
                                psPlanService.updateById(plan);
                            }
                        }
                    }
                    break;

                case Constant.TaskPlanType.LOOP_MONTH:
                    long month = DateUtil.betweenMonth(plan.getStartTime(), new Date(), false);
                    if (plan.getExecuteLastTime() != null){
                        // 执行过至少一次，检查2者的差值即可
                        // 3年一周期的情况下，第一年，余数1，2年余数2，三年余数0，四年余数1，5年余数2,6年余数0
                        // 应该先检查开始时间到现在时间相差的月份，然后除余，得到0则是这个月要执行的，再判断执行时间是否已经在这个月，在这个月则已经执行完成。否则判断是否超过月中的日的时间
                        long remainder = month % plan.getCycle();
                        if (remainder == 0){
                            // 在执行周期中
                            if (DateUtil.isIn(plan.getExecuteLastTime(), DateUtil.beginOfMonth(now), DateUtil.endOfMonth(now))){
                                // 一个月只会有一次，说明执行完了.

                            } else {
                                // 未执行完，判断当前日是否大于开始的日，否则已经逾期.yyyy-MM-dd HH:mm:ss
                                int startTimeDd = Integer.parseInt(DateUtil.format(plan.getStartTime(), "dd"));
                                if (nowDd > startTimeDd){
                                    // 已经过了开始时间，则进入逾期
                                    psPlanService.updateById(plan);

                                } else {
                                    // 未逾期
                                }
                            }
                        } else {
                            // 需要判断是否已经逾期,判断上次的执行时间到现在相隔的时间,然后是否大于周期
                            long executeLastTimeBetween = DateUtil.betweenMonth(plan.getExecuteLastTime(), new Date(), false);
                            long remainderBetween = executeLastTimeBetween % plan.getCycle();
                            int executeLastTimeDd = Integer.parseInt(DateUtil.format(plan.getExecuteLastTime(), "dd"));


                        }
                        if (month > 1){
                            if (month > plan.getCycle()){
                                psPlanService.updateById(plan);
                            }
                        }
                    } else {
                        // 从未执行过
                        if (month > 1){
                            if (month > plan.getCycle()){
                                psPlanService.updateById(plan);
                            }
                        }
                    }
                    break;

                case Constant.TaskPlanType.LOOP_DAY:
                    if (plan.getExecuteLastTime() != null){
                        // 执行过至少一次，检查2者的差值即可
                        long day = DateUtil.betweenDay(plan.getExecuteLastTime(), new Date(), false);
                        if (day > 1){
                            if (day > plan.getCycle()){
                                psPlanService.updateById(plan);
                            }
                        }
                    } else {
                        // 从未执行过
                        long day = DateUtil.betweenDay(plan.getStartTime(), new Date(), false);
                        // DateUtil.between(plan.getStartTime(), new Date(), DateUnit.DAY, false);
                        if (day > 1){
                            if (day > plan.getCycle()){
                                psPlanService.updateById(plan);
                            }
                        }
                    }
                    break;

                case Constant.TaskPlanType.LOOP_HOUR:
                    if (plan.getExecuteLastTime() != null){
                        // 执行过至少一次，检查2者的差值即可
                        long hour = DateUtil.between(plan.getExecuteLastTime(), new Date(), DateUnit.HOUR, false);
                        if (hour > 1){
                            if (hour > plan.getCycle()){
                                psPlanService.updateById(plan);
                            }
                        }
                    } else {
                        // 从未执行过
                        long hour = DateUtil.between(plan.getStartTime(), new Date(), DateUnit.HOUR, false);
                        if (hour > 1){
                            if (hour > plan.getCycle()){
                                psPlanService.updateById(plan);
                            }
                        }
                    }
                    break;

                default:
                    break;
            }

        }
    }
}
